
#include "stdafx.h"
#include <math.h>
#include "Picker.h"


CPicker::CPicker(LPDIRECT3DDEVICE9 pd3dDevice, HWND hwnd, CCamera* pCamera, CTerrain* pTerrain)
:m_d3dD3vice(pd3dDevice),
	m_hWnd(hwnd),
	m_pCamera(pCamera),
	m_pTerrain(pTerrain),
	m_isPicked(false),
	m_trianVertex1(0.0f, 0.0f, 0.0f),
	m_trianVertex2(0.0f, 0.0f, 0.0f),
	m_trianVertex3(0.0f, 0.0f, 0.0f),
	m_pickedPos(0.0f, 0.0f, 0.0f)
{

	m_pMapData = cgGame::CMapReader::GetMapDataInstance();
}



CPicker::~CPicker()
{

}



//Ray CPicker::CalcPickingRay(int xScreenPos, int yScreenPos)
Ray CPicker::CalcPickingRay()
{
	//! Get the screen point clicked.

	POINT screenPt;
	GetCursorPos(&screenPt);
	ScreenToClient(m_hWnd, &screenPt);
	//screenPt.x = xScreenPos;
	//screenPt.y = yScreenPos;
	//::GetCursorPos(&screenPt);
	////! Make it relative to the client area window.
	//ScreenToClient(g_d3dApp->GetMainWnd(),&screenPt);

	//! By the way we've been constructing things, the entire 
	//! backbuffer is the viewport.

	D3DVIEWPORT9 vp;
	m_d3dD3vice->GetViewport(&vp);
	int w = vp.Width;
	int h = vp.Height;

	D3DXMATRIX proj;
	m_d3dD3vice->GetTransform(D3DTS_PROJECTION, &proj);

	float x = ( 2.0f*screenPt.x/w - 1.0f) / proj(0,0);
	float y = (-2.0f*screenPt.y/h + 1.0f) / proj(1,1);

	// Build picking ray in view space.
	Ray ray;
	ray.origin = D3DXVECTOR3(0.0f, 0.0f, 0.0f);
	ray.dir    = D3DXVECTOR3(x, y, 1.0f);

	//! So if the view matrix transforms coordinates from 
	//! world space to view space, then the inverse of the
	//! view matrix transforms coordinates from view space
	//! to world space.
	D3DXMATRIX invView;
	D3DXMatrixInverse(&invView, 0, &m_pCamera->GetViewMatrix());

	//! Transform picking ray to world space.
	D3DXVec3TransformCoord (&ray.origin, &ray.origin, &invView);
	D3DXVec3TransformNormal(&ray.dir,    &ray.dir,    &invView);
	D3DXVec3Normalize(&ray.dir, &ray.dir);

	return ray;
}



void CPicker::Pick()
{
	m_isPicked = false;

	//VERTEXPNT* verts = m_pTerrain->GetVerts();
	int numVertexRow = m_pMapData->GetNumMapRow();//->GetRows();
	int numVertesCol = m_pMapData->GetNumMapCol();//->GetCols();
	float spacing    = static_cast<float>(m_pMapData->GetCellSpace());//->GetSpacing();

	Ray ray = CalcPickingRay();
	D3DXVECTOR3 vPointOnRay = ray.origin;
	float c = (vPointOnRay.x + 0.5f* numVertesCol*spacing ) /  spacing;
	float d = (vPointOnRay.z - 0.5f* numVertexRow*spacing ) / -spacing;
	//! Get the row and column we are in.
	int row = (int)floorf(d);
	int col = (int)floorf(c);
	int step= 0;
	while (InRange(col, 1, numVertesCol-2)
		&& InRange(row, 1, numVertexRow-2))
	{
		step++;
		vPointOnRay = ray.origin;
		vPointOnRay += static_cast<float>(step) * ray.dir;
		if (vPointOnRay.y <= m_pTerrain->GetHeight(vPointOnRay.x, vPointOnRay.z))
		{
			if (m_pTerrain->GetTriCell(static_cast<int >(vPointOnRay.x), 
					static_cast<int >(vPointOnRay.z)) == UPPER)
			{			
				m_trianVertex1 = m_pTerrain->GetTerrainPos(row, col);
				m_trianVertex2 = m_pTerrain->GetTerrainPos(row, col + 1);
				m_trianVertex3 = m_pTerrain->GetTerrainPos(row - 1, col);
				//m_trianVertex1 = verts[row*numVertexRow+col].position;
				//m_trianVertex2 = verts[row*numVertexRow+col + 1].position;
				//m_trianVertex3 = verts[(row+1)*numVertexRow+col].position;
			}
			else if (m_pTerrain->GetTriCell(static_cast<int >(vPointOnRay.x), 
					static_cast<int >( vPointOnRay.z)) == LOWER)
			{
				m_trianVertex1 = m_pTerrain->GetTerrainPos(row, col);
				m_trianVertex2 = m_pTerrain->GetTerrainPos(row, col - 1);
				m_trianVertex3 = m_pTerrain->GetTerrainPos(row + 1, col);
				//m_trianVertex1 = verts[row*numVertexRow+col].position;
				//m_trianVertex2 = verts[(row)*numVertexRow+col -1].position;
				//m_trianVertex3 = verts[(row-1)*numVertexRow+col].position;
			}
			m_isPicked  = true;
			m_pickedPos = vPointOnRay;
			break;
		}
		c = (vPointOnRay.x + 0.5f* numVertesCol*spacing ) /  spacing;
		d = (vPointOnRay.z - 0.5f* numVertexRow*spacing ) / -spacing;

		//! Get the row and column we are in.
		row = (int)floorf(d);
		col = (int)floorf(c);
	} 
}



D3DXVECTOR3 CPicker::GetPickingPos()
{
	return m_pickedPos;
}



BOOL CPicker::IntersectsSphere(Ray* ray, BoundingSphere* bs)
{
	/************************************************************************
	/ this based on how to figure out the two solutions of a formula
	/ discriminant = (b^2 - 4ac)/2a                                        
	/ sqrtD = sqrtf(discriminant)
	/ x1 = (-b + sqrtD)/2
	/ x2 = (-b - sqrtD)/2
	/************************************************************************/
	D3DXVECTOR3 v = ray->origin - bs->_center;

	float b = 2.0f * D3DXVec3Dot(&ray->dir, &v);
	float c = D3DXVec3Dot(&v, &v) - (bs->_radius * bs->_radius);

	//! find the discriminant
	float discriminant = (b * b) - (4.0f * c);

	//! test for imaginary number
	if( discriminant < 0.0f )
		return false;

	discriminant = sqrtf(discriminant);

	float s0 = (-b + discriminant) / 2.0f;
	float s1 = (-b - discriminant) / 2.0f;

	//! if a solution is >= 0, then we intersected the sphere
	if( s0 >= 0.0f || s1 >= 0.0f )
		return true;
	return false;
}




BOOL CPicker::IntersectsSphere(BoundingSphere* bs)
{
	/************************************************************************
	/ this based on how to figure out the two solutions of a formula
	/ discriminant = (b^2 - 4ac)/2a                                        
	/ sqrtD = sqrtf(discriminant)
	/ x1 = (-b + sqrtD)/2
	/ x2 = (-b - sqrtD)/2
	/************************************************************************/
	Ray tempRay = CalcPickingRay();
	Ray* ray = &tempRay;
	D3DXVECTOR3 v = ray->origin - bs->_center;

	float b = 2.0f * D3DXVec3Dot(&ray->dir, &v);
	float c = D3DXVec3Dot(&v, &v) - (bs->_radius * bs->_radius);

	//! find the discriminant
	float discriminant = (b * b) - (4.0f * c);

	//! test for imaginary number
	if( discriminant < 0.0f )
		return false;

	discriminant = sqrtf(discriminant);

	float s0 = (-b + discriminant) / 2.0f;
	float s1 = (-b - discriminant) / 2.0f;

	//! if a solution is >= 0, then we intersected the sphere
	if( s0 >= 0.0f || s1 >= 0.0f )
		return true;
	return false;
}